#![allow(dead_code)]

use std::{
    io,
    marker::Unpin,
    pin::Pin,
    task::{self, Poll},
};

use {futures_03_dep::ready, partial_io::PartialOp};

pub struct PartialAsyncRead<R> {
    inner: R,
    ops: Box<dyn Iterator<Item = PartialOp> + Send>,
}

impl<R> PartialAsyncRead<R>
where
    R: Unpin,
{
    pub fn new<I>(inner: R, ops: I) -> Self
    where
        I: IntoIterator<Item = PartialOp>,
        I::IntoIter: Send + 'static,
    {
        PartialAsyncRead {
            inner,
            ops: Box::new(ops.into_iter()),
        }
    }
}

impl<R> tokio_02_dep::io::AsyncRead for PartialAsyncRead<R>
where
    R: tokio_02_dep::io::AsyncRead + Unpin,
{
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut task::Context<'_>,
        buf: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        match self.ops.next() {
            Some(PartialOp::Limited(n)) => {
                let len = std::cmp::min(n, buf.len());
                Pin::new(&mut self.inner).poll_read(cx, &mut buf[..len])
            }
            Some(PartialOp::Err(err)) => {
                if err == io::ErrorKind::WouldBlock {
                    cx.waker().wake_by_ref();
                    Poll::Pending
                } else {
                    Err(io::Error::new(
                        err,
                        "error during read, generated by partial-io",
                    ))
                    .into()
                }
            }
            Some(PartialOp::Unlimited) | None => Pin::new(&mut self.inner).poll_read(cx, buf),
        }
    }
}

impl<R> tokio_03_dep::io::AsyncRead for PartialAsyncRead<R>
where
    R: tokio_03_dep::io::AsyncRead + Unpin,
{
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut task::Context<'_>,
        buf: &mut tokio_03_dep::io::ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        match self.ops.next() {
            Some(PartialOp::Limited(n)) => {
                let len = std::cmp::min(n, buf.remaining());
                buf.initialize_unfilled();
                let mut sub_buf = buf.take(len);
                ready!(Pin::new(&mut self.inner).poll_read(cx, &mut sub_buf))?;
                let filled = sub_buf.filled().len();
                buf.advance(filled);
                Poll::Ready(Ok(()))
            }
            Some(PartialOp::Err(err)) => {
                if err == io::ErrorKind::WouldBlock {
                    cx.waker().wake_by_ref();
                    Poll::Pending
                } else {
                    Err(io::Error::new(
                        err,
                        "error during read, generated by partial-io",
                    ))
                    .into()
                }
            }
            Some(PartialOp::Unlimited) | None => Pin::new(&mut self.inner).poll_read(cx, buf),
        }
    }
}

impl<R> tokio_dep::io::AsyncRead for PartialAsyncRead<R>
where
    R: tokio_dep::io::AsyncRead + Unpin,
{
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut task::Context<'_>,
        buf: &mut tokio_dep::io::ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        match self.ops.next() {
            Some(PartialOp::Limited(n)) => {
                let len = std::cmp::min(n, buf.remaining());
                buf.initialize_unfilled();
                let mut sub_buf = buf.take(len);
                ready!(Pin::new(&mut self.inner).poll_read(cx, &mut sub_buf))?;
                let filled = sub_buf.filled().len();
                buf.advance(filled);
                Poll::Ready(Ok(()))
            }
            Some(PartialOp::Err(err)) => {
                if err == io::ErrorKind::WouldBlock {
                    cx.waker().wake_by_ref();
                    Poll::Pending
                } else {
                    Err(io::Error::new(
                        err,
                        "error during read, generated by partial-io",
                    ))
                    .into()
                }
            }
            Some(PartialOp::Unlimited) | None => Pin::new(&mut self.inner).poll_read(cx, buf),
        }
    }
}

pub struct FuturesPartialAsyncRead<R> {
    inner: R,
    ops: Box<dyn Iterator<Item = PartialOp> + Send>,
}

impl<R> FuturesPartialAsyncRead<R>
where
    R: crate::futures::io::AsyncRead + Unpin,
{
    pub fn new<I>(inner: R, ops: I) -> Self
    where
        I: IntoIterator<Item = PartialOp>,
        I::IntoIter: Send + 'static,
    {
        FuturesPartialAsyncRead {
            inner,
            ops: Box::new(ops.into_iter()),
        }
    }
}

impl<R> crate::futures::io::AsyncRead for FuturesPartialAsyncRead<R>
where
    R: crate::futures::io::AsyncRead + Unpin,
{
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut task::Context<'_>,
        buf: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        match self.ops.next() {
            Some(PartialOp::Limited(n)) => {
                let len = std::cmp::min(n, buf.len());
                Pin::new(&mut self.inner).poll_read(cx, &mut buf[..len])
            }
            Some(PartialOp::Err(err)) => {
                if err == io::ErrorKind::WouldBlock {
                    cx.waker().wake_by_ref();
                    Poll::Pending
                } else {
                    Err(io::Error::new(
                        err,
                        "error during read, generated by partial-io",
                    ))
                    .into()
                }
            }
            Some(PartialOp::Unlimited) | None => Pin::new(&mut self.inner).poll_read(cx, buf),
        }
    }
}
